home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3.2
/
Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO
/
packet
/
n17jsrc
/
tcpsubr.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-03-18
|
7KB
|
332 lines
/* Low level TCP routines:
* control block management
* sequence number logical operations
* state transitions
* RTT cacheing
* garbage collection
*
* Copyright 1991 Phil Karn, KA9Q
*/
#include <stdio.h>
#include "global.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "tcp.h"
#include "ip.h"
/* TCP connection states */
char *Tcpstates[] = {
"",
"Closed",
"Listen",
"SYN sent",
"SYN received",
"Established",
"FIN wait 1",
"FIN wait 2",
"Close wait",
"Last ACK",
"Closing",
"Time wait"
};
/* TCP closing reasons */
char *Tcpreasons[] = {
"Normal",
"Reset/Refused",
"Timeout",
"ICMP"
};
struct tcb *Tcbs; /* Head of control block list */
int16 Tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */
int32 Tcp_irtt = DEF_RTT; /* Initial guess at round trip time */
int Tcp_trace; /* State change tracing flag */
int Tcp_syndata;
struct tcp_rtt Tcp_rtt[RTTCACHE];
struct mib_entry Tcp_mib[] = {
NULLCHAR, 0,
"tcpRtoAlgorithm", 4, /* Van Jacobsen's algorithm */
"tcpRtoMin", 0, /* No lower bound */
"tcpRtoMax", MAXINT32, /* No upper bound */
"tcpMaxConn", -1L, /* No limit */
"tcpActiveOpens", 0,
"tcpPassiveOpens", 0,
"tcpAttemptFails", 0,
"tcpEstabResets", 0,
"tcpCurrEstab", 0,
"tcpInSegs", 0,
"tcpOutSegs", 0,
"tcpRetransSegs", 0,
NULLCHAR, 0, /* Connection state goes here */
"tcpInErrs", 0,
"tcpOutRsts", 0,
};
/* Look up TCP connection
* Return TCB pointer or NULLTCB if nonexistant.
* Also move the entry to the top of the list to speed future searches.
*/
struct tcb *
lookup_tcb(conn)
register struct connection *conn;
{
register struct tcb *tcb;
struct tcb *tcblast = NULLTCB;
for(tcb=Tcbs;tcb != NULLTCB;tcblast = tcb,tcb = tcb->next){
/* Yet another structure compatibility hack */
if(conn->remote.port == tcb->conn.remote.port
&& conn->local.port == tcb->conn.local.port
&& conn->remote.address == tcb->conn.remote.address
&& conn->local.address == tcb->conn.local.address){
if(tcblast != NULLTCB){
/* Move to top of list */
tcblast->next = tcb->next;
tcb->next = Tcbs;
Tcbs = tcb;
}
return tcb;
}
}
return NULLTCB;
}
/* Create a TCB, return pointer. Return pointer if TCB already exists. */
struct tcb *
create_tcb(conn)
struct connection *conn;
{
register struct tcb *tcb;
struct tcp_rtt *tp;
if((tcb = lookup_tcb(conn)) != NULLTCB)
return tcb;
tcb = (struct tcb *)callocw(1,sizeof (struct tcb));
ASSIGN(tcb->conn,*conn);
tcb->state = TCP_CLOSED;
tcb->cwind = tcb->mss = Tcp_mss;
tcb->ssthresh = 65535;
if((tp = rtt_get(tcb->conn.remote.address)) != NULLRTT){
tcb->srtt = tp->srtt;
tcb->mdev = tp->mdev;
} else {
tcb->srtt = Tcp_irtt; /* mdev = 0 */
}
/* Initialize timer intervals */
set_timer(&tcb->timer,tcb->srtt);
tcb->timer.func = tcp_timeout;
tcb->timer.arg = tcb;
tcb->next = Tcbs;
Tcbs = tcb;
return tcb;
}
/* Close our TCB */
void
close_self(tcb,reason)
register struct tcb *tcb;
int reason;
{
struct reseq *rp1;
register struct reseq *rp;
if(tcb == NULLTCB)
return;
stop_timer(&tcb->timer);
tcb->reason = reason;
/* Flush reassembly queue; nothing more can arrive */
for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
rp1 = rp->next;
free_p(rp->bp);
free((char *)rp);
}
tcb->reseq = NULLRESEQ;
setstate(tcb,TCP_CLOSED);
}
/* Sequence number comparisons
* Return true if x is between low and high inclusive,
* false otherwise
*/
int
seq_within(x,low,high)
register int32 x,low,high;
{
if(low <= high){
if(low <= x && x <= high)
return 1;
} else {
if(low >= x && x >= high)
return 1;
}
return 0;
}
int
seq_lt(x,y)
register int32 x,y;
{
return (long)(x-y) < 0;
}
#ifdef notdef
int
seq_le(x,y)
register int32 x,y;
{
return (long)(x-y) <= 0;
}
#endif /* notdef */
int
seq_gt(x,y)
register int32 x,y;
{
return (long)(x-y) > 0;
}
int
seq_ge(x,y)
register int32 x,y;
{
return (long)(x-y) >= 0;
}
void
setstate(tcb,newstate)
register struct tcb *tcb;
register int newstate;
{
register char oldstate;
oldstate = tcb->state;
tcb->state = newstate;
if(Tcp_trace)
printf("TCB %lx %s -> %s\n",ptol(tcb),
Tcpstates[oldstate],Tcpstates[newstate]);
/* Update MIB variables */
switch(oldstate){
case TCP_CLOSED:
if(newstate == TCP_SYN_SENT)
tcpActiveOpens++;
break;
case TCP_LISTEN:
if(newstate == TCP_SYN_RECEIVED)
tcpPassiveOpens++;
break;
case TCP_SYN_SENT:
if(newstate == TCP_CLOSED)
tcpAttemptFails++;
break;
case TCP_SYN_RECEIVED:
switch(newstate){
case TCP_CLOSED:
case TCP_LISTEN:
tcpAttemptFails++;
break;
}
break;
case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT:
switch(newstate){
case TCP_CLOSED:
case TCP_LISTEN:
tcpEstabResets++;
break;
}
tcpCurrEstab--;
break;
}
if(newstate == TCP_ESTABLISHED || newstate == TCP_CLOSE_WAIT)
tcpCurrEstab++;
if(tcb->s_upcall)
(*tcb->s_upcall)(tcb,oldstate,newstate);
switch(newstate){
case TCP_SYN_RECEIVED: /***/
case TCP_ESTABLISHED:
/* Notify the user that he can begin sending data */
if(tcb->t_upcall)
(*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt);
break;
}
}
/* Round trip timing cache routines.
* These functions implement a very simple system for keeping track of
* network performance for future use in new connections.
* The emphasis here is on speed of update (rather than optimum cache hit
* ratio) since rtt_add is called every time a TCP connection updates
* its round trip estimate.
*/
void
rtt_add(addr,rtt)
int32 addr; /* Destination IP address */
int32 rtt;
{
register struct tcp_rtt *tp;
int32 abserr;
if(addr == 0)
return;
tp = &Tcp_rtt[(unsigned short)addr % RTTCACHE];
if(tp->addr != addr){
/* New entry */
tp->addr = addr;
tp->srtt = rtt;
tp->mdev = 0;
} else {
/* Run our own SRTT and MDEV integrators, with rounding */
abserr = (rtt > tp->srtt) ? rtt - tp->srtt : tp->srtt - rtt;
tp->srtt = ((AGAIN-1)*tp->srtt + rtt + (AGAIN/2)) >> LAGAIN;
tp->mdev = ((DGAIN-1)*tp->mdev + abserr + (DGAIN/2)) >> LDGAIN;
}
}
struct tcp_rtt *
rtt_get(addr)
int32 addr;
{
register struct tcp_rtt *tp;
if(addr == 0)
return NULLRTT;
tp = &Tcp_rtt[(unsigned short)addr % RTTCACHE];
if(tp->addr != addr)
return NULLRTT;
return tp;
}
/* TCP garbage collection - called by storage allocator when free space
* runs low. The send and receive queues are crunched. If the situation
* is red, the resequencing queue is discarded; otherwise it is
* also crunched.
*/
void
tcp_garbage(red)
int red;
{
register struct tcb *tcb;
struct reseq *rp,*rp1;
for(tcb = Tcbs;tcb != NULLTCB;tcb = tcb->next){
mbuf_crunch(&tcb->rcvq);
mbuf_crunch(&tcb->sndq);
for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
rp1 = rp->next;
if(red){
free_p(rp->bp);
free((char *)rp);
} else {
mbuf_crunch(&rp->bp);
}
}
if(red)
tcb->reseq = NULLRESEQ;
}
}